using System;
using System.Collections.Generic;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsEngineAddTest
    {
        public class Add
        {
            [Fact]
            public void NullLocalPath()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Add(null, true, null);
                                                     });
            }

            [Fact]
            public void PathNotFound()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();

                engine.Add(@"C:\Foo\bar.txt", true, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        callbackItem = item;
                                                        callbackResult = result;
                                                    });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_PathNotFound, callbackResult);
            }
        }

        public class Add_File
        {
            [Fact]
            public void AddFile()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File body");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\bar.txt", true, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        callbackItem = item;
                                                        callbackResult = result;
                                                    });

                Assert.Equal(Constants.NullItemId, callbackItem.ItemId);
                Assert.Equal(ItemType.File, callbackItem.ItemType);
                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(Constants.NullChangesetId, callbackItem.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Add, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
                Assert.True(engine.TfsState.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void AlreadyUnderSourceControl()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File body");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\bar.txt", true, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        callbackItem = item;
                                                        callbackResult = result;
                                                    });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_AlreadyUnderSourceControl, callbackResult);
            }

            [Fact]
            public void NotInAWorkingFolder()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File body");

                engine.Add(@"C:\Foo\bar.txt", true, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        callbackItem = item;
                                                        callbackResult = result;
                                                    });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_NotInAWorkingFolder, callbackResult);
            }

            [Fact]
            public void ValidatesDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File body");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\bar.txt", true, null);

                Assert.Equal(@"C:\Foo", engine.ValidateDirectoryStructure_Directory);
            }
        }

        public class Add_Folder
        {
            [Fact]
            public void AddFolderNonRecursive()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Baz", false, delegate(SourceItem item,
                                                          SourceItemResult result)
                                                 {
                                                     Assert.Null(callbackItem);
                                                     callbackItem = item;
                                                     callbackResult = result;
                                                 });

                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
                Assert.True(engine.TfsState.IsFolderTracked(@"C:\Foo\Baz"));
                Assert.False(engine.TfsState.IsFolderTracked(@"C:\Foo\Baz\Bar"));

                Assert.Equal(Constants.NullItemId, callbackItem.ItemId);
                Assert.Equal(ItemType.Folder, callbackItem.ItemType);
                Assert.Equal(@"C:\Foo\Baz", callbackItem.LocalName);
                Assert.Equal(SourceItemStatus.Add, callbackItem.LocalItemStatus);
                Assert.Equal(Constants.NullChangesetId, callbackItem.LocalChangesetId);

                SourceItem stateItem = engine.TfsState.GetSourceItem(@"C:\Foo\Baz");
                Assert.Equal(Constants.NullItemId, stateItem.ItemId);
                Assert.Equal(ItemType.Folder, stateItem.ItemType);
                Assert.Equal(@"C:\Foo\Baz", stateItem.LocalName);
                Assert.Equal(SourceItemStatus.Add, stateItem.LocalItemStatus);
                Assert.Equal(Constants.NullChangesetId, stateItem.LocalChangesetId);

                TfsFolderInfo folderInfo = engine.TfsState.GetFolderInfo(@"C:\Foo\Baz");
                Assert.Equal("http://tfsUrl", folderInfo.TfsUrl);
                Assert.Equal("$/Foo/Baz/", folderInfo.ServerPath);
            }

            [Fact]
            public void AddFolderRecursive()
            {
                int itemCount = 0;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\file1.txt", "Text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\file2.txt", "Text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\Bar\file3.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Baz", true, delegate
                                                {
                                                    ++itemCount;
                                                });

                Assert.Equal(5, itemCount);
            }

            [Fact]
            public void AddFolderRecursiveDoesNotAddMergeFiles()
            {
                int itemCount = 0;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\file1.txt", "Text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\file2.txt", "Text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\Bar\file3.txt", "Text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\Bar\file3.txt.mine", "Text");
                engine.FileSystem.WriteAllText(@"C:\Foo\Baz\Bar\file3.txt.r123", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Baz", true, delegate
                                                {
                                                    ++itemCount;
                                                });

                Assert.Equal(5, itemCount);
            }

            [Fact]
            public void AlreadyUnderSourceControl()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/Bar/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Bar", true, delegate(SourceItem item,
                                                         SourceItemResult result)
                                                {
                                                    callbackItem = item;
                                                    callbackResult = result;
                                                });

                Assert.Equal(@"C:\Foo\Bar", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_AlreadyUnderSourceControl, callbackResult);
            }

            [Fact]
            public void NotInWorkingFolder()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");

                engine.Add(@"C:\Foo\Bar", true, delegate(SourceItem item,
                                                         SourceItemResult result)
                                                {
                                                    callbackItem = item;
                                                    callbackResult = result;
                                                });

                Assert.Equal(@"C:\Foo\Bar", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_NotInAWorkingFolder, callbackResult);
            }

            [Fact]
            public void ValidatesDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Baz", false, null);

                Assert.Equal(@"C:\Foo", engine.ValidateDirectoryStructure_Directory);
            }
        }

        public class Add_IgnoreList
        {
            [Fact]
            public void ExplicitFileAddAcceptable()
            {
                List<string> addedItems = new List<string>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File body");
                engine.IgnoreList.Ignores.Add(@"C:\Foo\bar.txt");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\bar.txt", true, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        addedItems.Add(item.LocalName);
                                                    });

                Assert.Contains(@"C:\Foo\bar.txt", addedItems);
                Assert.True(engine.TfsState.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void ExplicitFolderAddAcceptable()
            {
                List<string> addedItems = new List<string>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.IgnoreList.Ignores.Add(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Bar", true, delegate(SourceItem item,
                                                         SourceItemResult result)
                                                {
                                                    addedItems.Add(item.LocalName);
                                                });

                Assert.Contains(@"C:\Foo\Bar", addedItems);
                Assert.True(engine.TfsState.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void PreventsFileAddInsideFolderAdd()
            {
                List<string> addedItems = new List<string>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\baz.txt", "Hi there");
                engine.IgnoreList.Ignores.Add(@"C:\Foo\Bar\baz.txt");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Bar", true, delegate(SourceItem item,
                                                         SourceItemResult result)
                                                {
                                                    addedItems.Add(item.LocalName);
                                                });

                Assert.False(addedItems.Contains(@"C:\Foo\Bar\baz.txt"));
                Assert.False(engine.TfsState.IsFileTracked(@"C:\Foo\Bar\baz.txt"));
            }

            [Fact]
            public void PreventsFolderAddInsideFolderAdd()
            {
                List<string> addedItems = new List<string>();
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar\Baz");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\Baz\biff.txt", "Hi there");
                engine.IgnoreList.Ignores.Add(@"C:\Foo\Bar\Baz");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Add(@"C:\Foo\Bar", true, delegate(SourceItem item,
                                                         SourceItemResult result)
                                                {
                                                    addedItems.Add(item.LocalName);
                                                });

                Assert.False(addedItems.Contains(@"C:\Foo\Bar\Baz"));
                Assert.False(addedItems.Contains(@"C:\Foo\Bar\Baz\biff.txt"));
                Assert.False(engine.TfsState.IsFolderTracked(@"C:\Foo\Bar\Baz"));
                Assert.False(engine.TfsState.IsFileTracked(@"C:\Foo\Bar\Baz\biff.txt"));
            }
        }
    }
}